package com.example.utils;

import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Description: RedisTemplate 工具类
 * @Author: mingtian
 * @CreateDate: 2019/11/18 10:03
 * @Version: 1.0
 */
@Slf4j
@Component
public class RedisTemplateUtil {

    /**
     * 超时时间
     */
    public static final Long TIME_OUT = 3600L;

    @Autowired
    private RedisTemplate redisTemplate;

    /***************************** 通用方法 *****************************************/
    /**
     * 返回所有的 key
     *
     * @return
     */
    public Set<String> getAllKeys() {
        Set keys = redisTemplate.keys("*");
        return keys;
    }

    /**
     * 获取 key 的类型
     *
     * @param key
     * @return
     */
    public DataType getType(String key) {
        return redisTemplate.type(key);
    }

    /**
     * 检查 key 是否存在
     *
     * @param key
     * @return
     */
    public Boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 判断 key 是否已经存在,如果存在则返回 false，否则返回 true 并新增 value
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean setNX(String key, String value) {
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(key, value);
        return aBoolean;
    }

    /**
     * 删除 key
     *
     * @param key
     * @return
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 返回失效时间
     *
     * @param key
     * @return
     */
    public Long getExpireTime(String key) {
        Long expire = redisTemplate.getExpire(key);
        return expire;
    }

    /********************************** 不同类型返回对应的 value  ********************************************/

    /**
     * 根据 key 获取对应的 value
     */
    public Map<String, Object> getAllString() {
        Set<String> allKeys = getAllKeys();
        Map<String, Object> map = new HashMap<>();
        Iterator<String> iterator = allKeys.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            DataType type = getType(next);
            if (type == DataType.STRING) {
                map.put(next, getByKey(next));
            }
        }
        return map;
    }
    /********************************* 存储对象 ***************************************/

    /**
     * 直接存储对象
     *
     * @param key
     * @param value
     * @param timeOut
     */
    public void add(String key, Object value, Long timeOut) {
        redisTemplate.opsForValue().set(key, value);
        if (timeOut != null) {
            Boolean expire = redisTemplate.expire(key, TIME_OUT, TimeUnit.SECONDS);
            log.info("expire:{}", expire);
        }
    }


    /**
     * 根据 key 获取 value
     *
     * @param key
     * @return
     */
    public Object getByKey(String key) {
        if (StringUtils.isEmpty(key)) {
            return "key is empty! ";
        }
        Object s = redisTemplate.opsForValue().get(key);
        log.info("getObject,key:{},value:{}", key, s);
        return s;
    }


    /**************************** List 类型操作 ************************************************/

    /**
     * 向 list 添加单个元素
     *
     * @param key
     * @param object
     * @return
     */
    public Long addList(String key, Object object) {
        return redisTemplate.opsForList().leftPush(key, object);
    }


    /**
     * 插入 list 集合
     *
     * @param key
     */
    public void addList(String key, List<Object> list) {
        for (Object o : list) {
            addList(key, o);
        }
        log.info("addList,key:{},list.size():{}", key, list.size());
    }

    /**
     * 插入 list 集合
     *
     * @param key
     * @param count
     */
    public void setList(String key, Integer count) {
        ArrayList<String> arrayList = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            arrayList.add(UUID.randomUUID().toString());
        }
        Long size = redisTemplate.opsForList().rightPushAll(key, arrayList);
        log.info("setList.size():{}", size);
    }


    /**
     * list 删除并返回第一个元素
     *
     * @param key
     * @return
     */
    public String getListOneAndDel(String key) {
        String o = String.valueOf(redisTemplate.opsForList().leftPop(key));
        log.info("getListOne,key:{},value:{}", key, o);
        return o;
    }

    /**
     * 返回 key 对应的数据数据
     *
     * @param key
     * @return
     */
    public Long getCount(String key) {
        Long size = redisTemplate.opsForList().size(key);
        log.info("key:{},size:{}", key, size);
        return size;
    }

    /**
     * 返回 list 集合
     *
     * @param key
     * @return
     */
    public List<Object> getList(String key) {
        List range = redisTemplate.opsForList().range(key, 0, getCount(key));
        log.info("getList,key:{},value:{}", key, range);
        return range;
    }

    /**
     * 返回 指定范围 list 集合
     *
     * @param key
     * @return
     */
    public List<Object> getList(String key, long start, long end) {
        List range = redisTemplate.opsForList().range(key, start, end);
        log.info("getList,key:{},value:{}", key, range);
        return range;
    }

    /**
     * 删除 list 集合中的 value
     *
     * @param key    key
     * @param object 要删除的value
     * @return
     */
    public long removeListValue(String key, Object object) {
        return redisTemplate.opsForList().remove(key, 0, object);
    }

    /**
     * 设置失效时间
     *
     * @param key
     * @param timeOut
     * @return
     */
    public Boolean setExpireTime(String key, long timeOut) {
        return redisTemplate.boundListOps(key).expire(timeOut, TimeUnit.SECONDS);
    }

    /**************************** Set 类型操作 ************************************************/

    /**
     * set类型 add操作
     *
     * @param key
     * @param value
     */
    public void setAdd(String key, Object value) {
        Long size = redisTemplate.opsForSet().add(key, value);
        log.info("setAdd,key:{},value:{},size:{}", key, value, size);
    }

    /**
     * 返回 Set 集合
     *
     * @param key
     * @return
     */
    public Set getSet(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 检查 set 集合中是否存在 对象
     *
     * @param key
     * @param object 要检查的对象
     * @return
     */
    public Boolean checkSetValue(String key, Object object) {
        return redisTemplate.opsForSet().isMember(key, object);
    }

    /**
     * 计算：第一个集合和所有连续集合之间的差得出的集合成员
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set differenceSet(String key, String otherKey) {
        log.info("set.difference,key1:{},value:{}", key, getSet(key));
        log.info("set.difference,key2:{},value:{}", otherKey, getSet(otherKey));
        Set difference = redisTemplate.opsForSet().difference(key, otherKey);

        log.info("set.difference,key1:{},key2:{}", key, otherKey);
        return difference;
    }

    /**
     * 计算多个 key 对应 value 的差集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set differenceSet(String key, String... otherKey) {
        ArrayList<String> arrayList = new ArrayList<>();
        for (String s : otherKey) {
            arrayList.add(s);
        }
        return redisTemplate.opsForSet().difference(key, arrayList);
    }

    /**
     * 将 key1、otherKey 的差集 存放到 destKey
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long differenceAndStore(String key, String otherKey, String destKey) {
        log.info("key:{},otherKey:{},destKey:{}", key, otherKey, destKey);
        Long aLong = redisTemplate.opsForSet().differenceAndStore(key, otherKey, destKey);
        return aLong;
    }

    /**
     * 计算 key 与 otherKey 的交集
     *
     * @param key
     * @param otherKey
     */
    public Set intersect(String key, String otherKey) {
        log.info("set.intersect,key:{},value:{}", key, getSet(key));
        log.info("set.intersect,otherKey:{},value:{}", key, getSet(otherKey));
        Set intersect = redisTemplate.opsForSet().intersect(key, otherKey);
        return intersect;
    }

    /**
     * 计算 多个 key 的交集
     *
     * @param key
     * @param otherKey
     */
    public Set intersect(String key, String... otherKey) {
        ArrayList<String> arrayList = new ArrayList<>();
        for (String s : otherKey) {
            arrayList.add(s);
            log.info("set.intersect,key:{},value:{}", key, getSet(key));
        }
        Set intersect = redisTemplate.opsForSet().intersect(key, arrayList);
        return intersect;
    }

    /**
     * 计算 key 与 otherKey 的并集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set union(String key, String otherKey) {
        return redisTemplate.opsForSet().union(key, otherKey);
    }

    /******************************* ZSet 类型 ****************************************/
    public static Integer integer = 0;

    /**
     * ZSet add
     *
     * @param key
     * @param set
     */
    public void addZSet(String key, Set<Object> set) {

        Boolean add = redisTemplate.opsForZSet().add(key, set, integer++);
        log.info("ZSet.add,key:{},value:{},result:{}", key, set, add);
    }

    /**
     * 返回 start-end 的集合
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Object> getZSet(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }


    /******************************* Hash 类型 ****************************************/
    /**
     * 存放 hash 类型数据
     *
     * @param key
     * @param map
     */
    public void setHash(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 根据 key 查询
     *
     * @param key
     * @return
     */
    public Map getHashKey(String key) {
        Map entries = redisTemplate.opsForHash().entries(key);
        return entries;
    }

    /**
     * 获取 key 对应的 value
     *
     * @param key
     * @return
     */
    public List getHashValues(String key) {
        List values = redisTemplate.opsForHash().values(key);
        return values;
    }
}